package com.lagou.spring;

import com.lagou.edu.factory.ProxyFactory;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ApplicationContext {

    private Class configClass;

    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();//单例池
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    private ArrayList<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();


    public ApplicationContext(Class configClass) {
        this.configClass = configClass;
        //解析配置类
        //ComponetnSpan注解 -> 扫描路径-> 扫描 ->BeanDefinition ->beanDefinitionMap
        scanConfig(configClass);

        //如果是单例的，创建bean对象
        for (Map.Entry<String, BeanDefinition> entrys : beanDefinitionMap.entrySet()) {
            String beanName = entrys.getKey();
            BeanDefinition beanDefinition = entrys.getValue();
            String scope = beanDefinition.getScope();
            //已经注入过的不需要重新创建
            if ("singleton".equals(scope) && !singletonObjects.containsKey(beanName)) {
                Object bean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);
            }
        }

    }

    private void scanConfig(Class configClass) {
        ComponentScan declaredComponentScan = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = declaredComponentScan.value();//扫描路径
        ClassLoader classLoader = ApplicationContext.class.getClassLoader();
        //AppClassLoader
        URL resource = classLoader.getResource(path.replace(".", "/"));
        File files = new File(resource.getFile());
        digui(files, classLoader);
    }

    private void digui(File files, ClassLoader classLoader) {
        if (files.isDirectory()) {
            File[] fileArray = files.listFiles();
            for (File file : fileArray) {
                digui(file, classLoader);
            }
        } else {
            //封装BeanDefiniton
            storeBeanDefiniton(files, classLoader);
        }
    }

    private void storeBeanDefiniton(File file, ClassLoader classLoader) {
        String absolutePath = file.getAbsolutePath();
        if (!absolutePath.contains(".class")) {
            return;
        }
        //servlet加载异常
        if (absolutePath.contains("servlet")) {
            return;
        }
        String packageName = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
        packageName = packageName.replaceAll("\\/", ".");
        Class<?> aClass = null;
        try {
            aClass = classLoader.loadClass(packageName);
            if (aClass.isAnnotationPresent(Component.class)) {
                if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                    try {
                        BeanPostProcessor objBeanPostProcessor = (BeanPostProcessor) aClass.getDeclaredConstructor().newInstance();
                        beanPostProcessorList.add(objBeanPostProcessor);
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    }

                }

                //Component注解说明是一个bean
                BeanDefinition beanDefinition = new BeanDefinition();
                beanDefinition.setCalzz(aClass);
                String beanName = aClass.getDeclaredAnnotation(Component.class).value();
                //没有配置默认值的情况,首字母小写
                if ("".equals(beanName)) {
                    String name = aClass.getName();
                    beanName = name.substring(name.lastIndexOf(".") + 1, name.length());
                    if (!Character.isLowerCase(beanName.charAt(0)))
                        beanName = (new StringBuilder()).append(Character.toLowerCase(beanName.charAt(0))).append(beanName.substring(1)).toString();
                }
                if (aClass.isAnnotationPresent(Scope.class)) {
                    Scope declaredAnnotation = aClass.getDeclaredAnnotation(Scope.class);
                    beanDefinition.setScope(declaredAnnotation.value());
                } else {
                    beanDefinition.setScope("singleton");
                }
                beanDefinitionMap.put(beanName, beanDefinition);

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Object getBean(String beanName) {
        Object obj = null;
        if (beanDefinitionMap.containsKey(beanName)) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            String scope = beanDefinition.getScope();


            //判断是否是单例的，是则
            if ("singleton".equals(scope) && beanDefinitionMap.containsKey(beanName) && singletonObjects.containsKey(beanName)) {
                obj = singletonObjects.get(beanName);
            } else {
                obj = createBean(beanName, beanDefinition);
            }
            //单例存入单例池
            if ("singleton".equals(scope)) {
                singletonObjects.put(beanName, obj);
            }


            if (obj != null) {
                //判断是否是有Transactional注解,有的话返回动态代理对象
                Class<?> aClass = obj.getClass();
                if (aClass.isAnnotationPresent(Transactional.class)) {
                    //判断是否有接口
                    Class<?>[] interfaces = aClass.getInterfaces();
                    ProxyFactory proxyFactory = (ProxyFactory) getBean("proxyFactory");
                    if (interfaces.length > 0) {
                        return proxyFactory.getJdkProxy(obj);
                    } else {
                        return proxyFactory.getCglibProxy(obj);
                    }
                }
            }

        }
        return obj;
    }

    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class calzz = beanDefinition.getCalzz();
        Constructor constructor = null;
        try {
            constructor = calzz.getConstructor();
            Object obj = constructor.newInstance();

            //依赖注入
            Field[] declaredFields = calzz.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                if (declaredField.isAnnotationPresent(Autowired.class)) {
                    declaredField.setAccessible(true);
                    declaredField.set(obj, getBean(declaredField.getName()));
                }
            }
            //Aware回调
            if (obj instanceof BeanNameAware) {
                ((BeanNameAware) obj).setBeanName(beanName);

            }
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                obj = beanPostProcessor.postProcessBeforeInitialization(obj, beanName);
            }

            //初始化
            if (obj instanceof InitializingBean) {
                try {
                    ((InitializingBean) obj).afterPropertiesSet();
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                obj = beanPostProcessor.postProcessAfterInitialization(obj, beanName);
            }


            return obj;
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }


}
